package wxpay

import (
	"bgs/db"
	sqlc "bgs/db/sqlc"
	nsqpUtil "bgs/nsqp/util"
	brandService "bgs/service/brand"
	ledgerService "bgs/service/ledger"
	orderService "bgs/service/order"
	storeService "bgs/service/store"
	userService "bgs/service/user"
	"bgs/util"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/nsqio/go-nsq"
	"go.uber.org/zap"
)

// var (
// 	lastProfitOrderNotification *ProfitOrderNotification = nil
// )

// ProfitReceiver 分账接收者
type ProfitReceiver struct {
	// 分账接收方类型
	Type string `json:"type"`
	// 分账接收方帐号
	Account string `json:"account"`
	// 分账动账金额
	Amount int64 `json:"amount"`
	// 分账/回退描述
	Description string `json:"description"`
	// 分账接受方姓名
	ReceiverName string `json:"receiver_name"`
}

// ProfitOrderNotification 分账变更消息
type ProfitOrderNotification struct {
	// 品牌id
	BrandID string `json:"brand_id"`
	// 商户订单编号
	OutTradeNo string `json:"out_trade_no"`
	// 商户分账单号
	OutOrderNo string `json:"out_order_no"`
	// 二级商户商户号
	SubMchID string `json:"sub_mch_id"`
	// 分账接受方
	Receivers []ProfitReceiver `json:"receivers"`
	// 是否完结分账
	Finish bool `json:"finish"`
	// 微信分账单号
	WxOrderID string `json:"wx_order_id"`
	// 服务商商户号
	MchID string `json:"mch_id"`
	// 服务商appid
	AppID string `json:"app_id"`
	// 微信支付单号
	WxTransID string `json:"wx_trans_id"`
	// 分账完结描述(仅分账完结有)
	FinishDescription string `json:"finish_description,omitempty"`
	// 分账失败原因(仅分账失败有)
	CloseReason string `json:"close_reason,omitempty"`
	// 分账完成金额(仅分账完结有)
	FinishAmount string `json:"finish_amount,omitempty"`
	// 分账单状态
	Status string `json:"status"`
	// 消息时间戳
	Timestamp int64 `json:"timestamp"`
}

// ProfitOrderHandler wxpay profit order message handler
type ProfitOrderHandler struct {
	dao           *db.PgDao
	slog          *zap.SugaredLogger
	topic         string
	failover      *nsqpUtil.NsqFailover
	brandService  *brandService.BrandServiceImp
	ledgerService *ledgerService.LedgerServiceImp
	orderService  *orderService.OrderServiceImp
	storeService  *storeService.StoreServiceImp
	userService   *userService.UserServiceImp
}

// NewProfitOrderHandler create a new handler for profit order
func NewProfitOrderHandler(
	pgdao *db.PgDao,
	slog *zap.SugaredLogger,
	topic string,
	failover *nsqpUtil.NsqFailover,
	brandService *brandService.BrandServiceImp,
	ledgerService *ledgerService.LedgerServiceImp,
	orderService *orderService.OrderServiceImp,
	storeService *storeService.StoreServiceImp,
	userService *userService.UserServiceImp,
) *ProfitOrderHandler {
	return &ProfitOrderHandler{
		dao:           pgdao,
		slog:          slog.With("nsq_consumer_handler", "ProfitOrderHandler"),
		topic:         topic,
		failover:      failover,
		brandService:  brandService,
		ledgerService: ledgerService,
		orderService:  orderService,
		storeService:  storeService,
		userService:   userService,
	}
}

// HandleMessage implements the Handler interface.
func (h *ProfitOrderHandler) HandleMessage(m *nsq.Message) error {
	if len(m.Body) == 0 {
		// Returning nil will automatically send a FIN command to NSQ to mark the message as processed.
		return nil
	}

	h.slog.Infof("message=>%s", string(m.Body))

	v := &ProfitOrderNotification{}
	if decodeErr := json.NewDecoder(bytes.NewReader(m.Body)).Decode(v); decodeErr != nil {
		return fmt.Errorf("无法解析消息[ProfitOrderNotification]: %s", decodeErr.Error())
	}

	// if lastProfitOrderNotification != nil && lastProfitOrderNotification.Timestamp >= v.Timestamp {
	// 	h.slog.Infof("过时的消息: lastProfitOrderNotification.Timestamp:%v,v.Timestamp:%v", lastProfitOrderNotification.Timestamp, v.Timestamp)
	// 	return nil
	// }
	reqID := strconv.FormatInt(v.Timestamp, 10)
	slog := h.slog.With("reqID", reqID)
	// 将来抽象到对应的业务组件
	ctx := context.WithValue(context.Background(), util.RequestID{}, reqID)

	var err error
	defer h.failover.BackupSubMsgWhenHandleFail(ctx, &err, nsqpUtil.BackupSubMsgParam{
		Ts:    v.Timestamp,
		Topic: h.topic,
		Mbody: m.Body,
	})

	wxLedgerOrderNo := v.WxOrderID
	ledgerSerialNumber := v.OutOrderNo
	orderSerialNumber := v.OutTradeNo
	occurred := time.Now()
	// finished := v.Finish // 不用了,因为不是走wxpay的FinishSubmit模式
	err = h.dao.NewTxRunner().SqlcExecTx(ctx, func(q *sqlc.Queries) (err error) {
		ledgerRequestPtr, err := h.ledgerService.DoAfterRequestSubmitSuccess(ctx, q, ledgerService.AfterRequestSubmitSuccessParam{
			WxLedgerOrderNo:    wxLedgerOrderNo,
			LedgerSerialNumber: ledgerSerialNumber,
			OrderSerialNumber:  orderSerialNumber,
			Occurred:           occurred,
		})
		if err != nil {
			return
		}
		slog.Infof("a 本地分账请求完成 ok")

		order, err := q.GetOrderBySerialNumber(ctx, ledgerRequestPtr.OrderSerialNumber)
		if err != nil {
			return
		}
		slog.Infof("b 找到分账的订单 ok")
		orderPtr := &order

		if ledgerRequestPtr.DistributorCommission > 0 {
			slog.Infof("c 分销订单佣金需要记账")
			err = h.brandService.LoggingAccountForOrderDistributorCommissionProfit(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
			if err != nil {
				return
			}
			slog.Infof("c1 商家分销订单佣金记账 ok")

			err = h.storeService.LoggingAccountForOrderDistributorCommissionProfit(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
			if err != nil {
				return
			}
			slog.Infof("c2 分销员分销订单奖励金记账 ok")
		}

		if ledgerRequestPtr.BonusAmount > 0 {
			if orderPtr.StoreID.Valid {
				slog.Infof("c 门店订单奖励金需要记账")
				err = h.storeService.LoggingAccountForOrderBonusProfit(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("c1 门店订单奖励金记账 ok")

				err = h.userService.LoggingAccountForOrderBonusProfitOfStore(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("c4 用户推广门店订单奖励金记账 ok")
			} else {
				slog.Infof("c 品牌订单奖励金需要记账")
				err = h.brandService.LoggingAccountForOrderBonusProfit(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("c3 品牌订单奖励金记账 ok")

				err = h.userService.LoggingAccountForOrderBonusProfitOfBrand(ctx, q, ledgerRequestPtr, orderPtr, wxLedgerOrderNo, occurred)
				if err != nil {
					return
				}
				slog.Infof("c4 用户推广品牌订单奖励金记账 ok")
			}

			spokesmanUserID := order.SpokesmanUserID.Int64
			userAccount, lockErr := q.GetUserAccountByUserIDToLock(ctx, spokesmanUserID)
			if lockErr != nil {
				err = lockErr
				return
			}
			slog.Infof("e 获取带写入锁的用户账号 ok")

			_, err = h.userService.UpdateAccountPendingAmountForOrderBonusWithLock(ctx, q, userService.UpdateAccountPendingAmountForOrderBonusWithLockParam{
				UserAccountPtr:        &userAccount,
				OrderBonusAmountDelta: ledgerRequestPtr.BonusAmount,
			})
			if err != nil {
				return
			}
			slog.Infof("f 用户总账更新冻结的奖励金 ok")
		}

		err = h.ledgerService.MarkLedgerRequestAccounted(ctx, q, ledgerRequestPtr.SerialNumber)
		if err != nil {
			return
		}
		slog.Infof("g 标记分账请求已记账 ok")
		slog.Infof("h (%s)分账结束 ok", ledgerSerialNumber)

		if ledgerRequestPtr.LastFlag {
			// MarkOrderProfited
			err = h.orderService.MarkOrderProfited(ctx, q, order.ID)
			if err != nil {
				return
			}
			slog.Infof("i 标记订单(%s)已分账 ok", orderSerialNumber)
		} else {
			slog.Infof("i 订单(%s)分账未结束 ok", orderSerialNumber)
		}
		return
	})

	if err != nil {
		return err
	}
	slog.Infof("消息[ProfitOrderNotification]处理成功")

	// lastProfitOrderNotification = v
	// Returning a non-nil error will automatically send a REQ command to NSQ to re-queue the message.
	return nil
}
